/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- * vim: set ts=8 sts=4 et sw=4 tw=99: * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */#include"jit/LIR.h"#include<ctype.h>#include"jsprf.h"#include"jit/JitSpewer.h"#include"jit/MIR.h"#include"jit/MIRGenerator.h"usingnamespacejs;usingnamespacejs::jit;LIRGraph::LIRGraph(MIRGraph*mir):blocks_(),constantPool_(mir->alloc()),constantPoolMap_(mir->alloc()),safepoints_(mir->alloc()),nonCallSafepoints_(mir->alloc()),numVirtualRegisters_(0),numInstructions_(1),// First id is 1.localSlotCount_(0),argumentSlotCount_(0),entrySnapshot_(nullptr),mir_(*mir){}boolLIRGraph::addConstantToPool(constValue&v,uint32_t*index){MOZ_ASSERT(constantPoolMap_.initialized());ConstantPoolMap::AddPtrp=constantPoolMap_.lookupForAdd(v);if(p){*index=p->value();returntrue;}*index=constantPool_.length();returnconstantPool_.append(v)&&constantPoolMap_.add(p,v,*index);}boolLIRGraph::noteNeedsSafepoint(LInstruction*ins){// Instructions with safepoints must be in linear order.MOZ_ASSERT_IF(!safepoints_.empty(),safepoints_.back()->id()<ins->id());if(!ins->isCall()&&!nonCallSafepoints_.append(ins))returnfalse;returnsafepoints_.append(ins);}voidLIRGraph::dump(GenericPrinter&out){for(size_ti=0;i<numBlocks();i++){getBlock(i)->dump(out);out.printf("\n");}}voidLIRGraph::dump(){Fprinterout(stderr);dump(out);out.finish();}LBlock::LBlock(MBasicBlock*from):block_(from),phis_(),entryMoveGroup_(nullptr),exitMoveGroup_(nullptr){from->assignLir(this);}boolLBlock::init(TempAllocator&alloc){// Count the number of LPhis we'll need.size_tnumLPhis=0;for(MPhiIteratori(block_->phisBegin()),e(block_->phisEnd());i!=e;++i){MPhi*phi=*i;switch(phi->type()){caseMIRType::Value:numLPhis+=BOX_PIECES;break;caseMIRType::Int64:numLPhis+=INT64_PIECES;break;default:numLPhis+=1;break;}}// Allocate space for the LPhis.if(!phis_.init(alloc,numLPhis))returnfalse;// For each MIR phi, set up LIR phis as appropriate. We'll fill in their// operands on each incoming edge, and set their definitions at the start of// their defining block.size_tphiIndex=0;size_tnumPreds=block_->numPredecessors();for(MPhiIteratori(block_->phisBegin()),e(block_->phisEnd());i!=e;++i){MPhi*phi=*i;MOZ_ASSERT(phi->numOperands()==numPreds);intnumPhis;switch(phi->type()){caseMIRType::Value:numPhis=BOX_PIECES;break;caseMIRType::Int64:numPhis=INT64_PIECES;break;default:numPhis=1;break;}for(inti=0;i<numPhis;i++){LAllocation*inputs=alloc.allocateArray<LAllocation>(numPreds);if(!inputs)returnfalse;void*addr=&phis_[phiIndex++];LPhi*lphi=new(addr)LPhi(phi,inputs);lphi->setBlock(this);}}returntrue;}constLInstruction*LBlock::firstInstructionWithId()const{for(LInstructionIteratori(instructions_.begin());i!=instructions_.end();++i){if(i->id())return*i;}return0;}LMoveGroup*LBlock::getEntryMoveGroup(TempAllocator&alloc){if(entryMoveGroup_)returnentryMoveGroup_;entryMoveGroup_=LMoveGroup::New(alloc);insertBefore(*begin(),entryMoveGroup_);returnentryMoveGroup_;}LMoveGroup*LBlock::getExitMoveGroup(TempAllocator&alloc){if(exitMoveGroup_)returnexitMoveGroup_;exitMoveGroup_=LMoveGroup::New(alloc);insertBefore(*rbegin(),exitMoveGroup_);returnexitMoveGroup_;}voidLBlock::dump(GenericPrinter&out){out.printf("block%u:\n",mir()->id());for(size_ti=0;i<numPhis();++i){getPhi(i)->dump(out);out.printf("\n");}for(LInstructionIteratoriter=begin();iter!=end();iter++){iter->dump(out);out.printf("\n");}}voidLBlock::dump(){Fprinterout(stderr);dump(out);out.finish();}staticsize_tTotalOperandCount(LRecoverInfo*recoverInfo){size_taccum=0;for(LRecoverInfo::OperandIterit(recoverInfo);!it;++it){if(!it->isRecoveredOnBailout())accum++;}returnaccum;}LRecoverInfo::LRecoverInfo(TempAllocator&alloc):instructions_(alloc),recoverOffset_(INVALID_RECOVER_OFFSET){}LRecoverInfo*LRecoverInfo::New(MIRGenerator*gen,MResumePoint*mir){LRecoverInfo*recoverInfo=new(gen->alloc())LRecoverInfo(gen->alloc());if(!recoverInfo||!recoverInfo->init(mir))returnnullptr;JitSpew(JitSpew_IonSnapshots,"Generating LIR recover info %p from MIR (%p)",(void*)recoverInfo,(void*)mir);returnrecoverInfo;}boolLRecoverInfo::appendOperands(MNode*ins){for(size_ti=0,end=ins->numOperands();i<end;i++){MDefinition*def=ins->getOperand(i);// As there is no cycle in the data-flow (without MPhi), checking for// isInWorkList implies that the definition is already in the// instruction vector, and not processed by a caller of the current// function.if(def->isRecoveredOnBailout()&&!def->isInWorklist()){if(!appendDefinition(def))returnfalse;}}returntrue;}boolLRecoverInfo::appendDefinition(MDefinition*def){MOZ_ASSERT(def->isRecoveredOnBailout());def->setInWorklist();if(!appendOperands(def))returnfalse;returninstructions_.append(def);}boolLRecoverInfo::appendResumePoint(MResumePoint*rp){// Stores should be recovered first.for(autoiter(rp->storesBegin()),end(rp->storesEnd());iter!=end;++iter){if(!appendDefinition(iter->operand))returnfalse;}if(rp->caller()&&!appendResumePoint(rp->caller()))returnfalse;if(!appendOperands(rp))returnfalse;returninstructions_.append(rp);}boolLRecoverInfo::init(MResumePoint*rp){// Sort operations in the order in which we need to restore the stack. This// implies that outer frames, as well as operations needed to recover the// current frame, are located before the current frame. The inner-most// resume point should be the last element in the list.if(!appendResumePoint(rp))returnfalse;// Remove temporary flags from all definitions.for(MNode**it=begin();it!=end();it++){if(!(*it)->isDefinition())continue;(*it)->toDefinition()->setNotInWorklist();}MOZ_ASSERT(mir()==rp);returntrue;}LSnapshot::LSnapshot(LRecoverInfo*recoverInfo,BailoutKindkind):numSlots_(TotalOperandCount(recoverInfo)*BOX_PIECES),slots_(nullptr),recoverInfo_(recoverInfo),snapshotOffset_(INVALID_SNAPSHOT_OFFSET),bailoutId_(INVALID_BAILOUT_ID),bailoutKind_(kind){}boolLSnapshot::init(MIRGenerator*gen){slots_=gen->allocate<LAllocation>(numSlots_);return!!slots_;}LSnapshot*LSnapshot::New(MIRGenerator*gen,LRecoverInfo*recover,BailoutKindkind){LSnapshot*snapshot=new(gen->alloc())LSnapshot(recover,kind);if(!snapshot||!snapshot->init(gen))returnnullptr;JitSpew(JitSpew_IonSnapshots,"Generating LIR snapshot %p from recover (%p)",(void*)snapshot,(void*)recover);returnsnapshot;}voidLSnapshot::rewriteRecoveredInput(LUseinput){// Mark any operands to this snapshot with the same value as input as being// equal to the instruction's result.for(size_ti=0;i<numEntries();i++){if(getEntry(i)->isUse()&&getEntry(i)->toUse()->virtualRegister()==input.virtualRegister())setEntry(i,LUse(input.virtualRegister(),LUse::RECOVERED_INPUT));}}voidLNode::printName(GenericPrinter&out,Opcodeop){staticconstchar*constnames[]={#define LIROP(x) #x,LIR_OPCODE_LIST(LIROP)#undef LIROP};constchar*name=names[op];size_tlen=strlen(name);for(size_ti=0;i<len;i++)out.printf("%c",tolower(name[i]));}voidLNode::printName(GenericPrinter&out){printName(out,op());}boolLAllocation::aliases(constLAllocation&other)const{if(isFloatReg()&&other.isFloatReg())returntoFloatReg()->reg().aliases(other.toFloatReg()->reg());return*this==other;}staticconstchar*typeName(LDefinition::Typetype){switch(type){caseLDefinition::GENERAL:return"g";caseLDefinition::INT32:return"i";caseLDefinition::OBJECT:return"o";caseLDefinition::SLOTS:return"s";caseLDefinition::FLOAT32:return"f";caseLDefinition::DOUBLE:return"d";caseLDefinition::SIMD128INT:return"simd128int";caseLDefinition::SIMD128FLOAT:return"simd128float";caseLDefinition::SINCOS:return"sincos";#ifdef JS_NUNBOX32caseLDefinition::TYPE:return"t";caseLDefinition::PAYLOAD:return"p";#elsecaseLDefinition::BOX:return"x";#endif}MOZ_CRASH("Invalid type");}UniqueCharsLDefinition::toString()const{AutoEnterOOMUnsafeRegionoomUnsafe;UniqueCharsbuf;if(isBogusTemp()){buf=JS_smprintf("bogus");}else{buf=JS_smprintf("v%u<%s>",virtualRegister(),typeName(type()));if(buf){if(policy()==LDefinition::FIXED)buf=JS_sprintf_append(Move(buf),":%s",output()->toString().get());elseif(policy()==LDefinition::MUST_REUSE_INPUT)buf=JS_sprintf_append(Move(buf),":tied(%u)",getReusedInput());}}if(!buf)oomUnsafe.crash("LDefinition::toString()");returnbuf;}staticUniqueCharsPrintUse(constLUse*use){switch(use->policy()){caseLUse::REGISTER:returnJS_smprintf("v%d:r",use->virtualRegister());caseLUse::FIXED:returnJS_smprintf("v%d:%s",use->virtualRegister(),AnyRegister::FromCode(use->registerCode()).name());caseLUse::ANY:returnJS_smprintf("v%d:r?",use->virtualRegister());caseLUse::KEEPALIVE:returnJS_smprintf("v%d:*",use->virtualRegister());caseLUse::RECOVERED_INPUT:returnJS_smprintf("v%d:**",use->virtualRegister());default:MOZ_CRASH("invalid use policy");}}UniqueCharsLAllocation::toString()const{AutoEnterOOMUnsafeRegionoomUnsafe;UniqueCharsbuf;if(isBogus()){buf=JS_smprintf("bogus");}else{switch(kind()){caseLAllocation::CONSTANT_VALUE:caseLAllocation::CONSTANT_INDEX:buf=JS_smprintf("c");break;caseLAllocation::GPR:buf=JS_smprintf("%s",toGeneralReg()->reg().name());break;caseLAllocation::FPU:buf=JS_smprintf("%s",toFloatReg()->reg().name());break;caseLAllocation::STACK_SLOT:buf=JS_smprintf("stack:%d",toStackSlot()->slot());break;caseLAllocation::ARGUMENT_SLOT:buf=JS_smprintf("arg:%d",toArgument()->index());break;caseLAllocation::USE:buf=PrintUse(toUse());break;default:MOZ_CRASH("what?");}}if(!buf)oomUnsafe.crash("LAllocation::toString()");returnbuf;}voidLAllocation::dump()const{fprintf(stderr,"%s\n",toString().get());}voidLDefinition::dump()const{fprintf(stderr,"%s\n",toString().get());}voidLNode::printOperands(GenericPrinter&out){for(size_ti=0,e=numOperands();i<e;i++){out.printf(" (%s)",getOperand(i)->toString().get());if(i!=numOperands()-1)out.printf(",");}}voidLInstruction::assignSnapshot(LSnapshot*snapshot){MOZ_ASSERT(!snapshot_);snapshot_=snapshot;#ifdef JS_JITSPEWif(JitSpewEnabled(JitSpew_IonSnapshots)){JitSpewHeader(JitSpew_IonSnapshots);Fprinter&out=JitSpewPrinter();out.printf("Assigning snapshot %p to instruction %p (",(void*)snapshot,(void*)this);printName(out);out.printf(")\n");}#endif}voidLNode::dump(GenericPrinter&out){if(numDefs()!=0){out.printf("{");for(size_ti=0;i<numDefs();i++){out.printf("%s",getDef(i)->toString().get());if(i!=numDefs()-1)out.printf(", ");}out.printf("} <- ");}printName(out);printOperands(out);if(numTemps()){out.printf(" t=(");for(size_ti=0;i<numTemps();i++){out.printf("%s",getTemp(i)->toString().get());if(i!=numTemps()-1)out.printf(", ");}out.printf(")");}if(numSuccessors()){out.printf(" s=(");for(size_ti=0;i<numSuccessors();i++){out.printf("block%u",getSuccessor(i)->id());if(i!=numSuccessors()-1)out.printf(", ");}out.printf(")");}}voidLNode::dump(){Fprinterout(stderr);dump(out);out.printf("\n");out.finish();}voidLInstruction::initSafepoint(TempAllocator&alloc){MOZ_ASSERT(!safepoint_);safepoint_=new(alloc)LSafepoint(alloc);MOZ_ASSERT(safepoint_);}boolLMoveGroup::add(LAllocationfrom,LAllocationto,LDefinition::Typetype){#ifdef DEBUGMOZ_ASSERT(from!=to);for(size_ti=0;i<moves_.length();i++)MOZ_ASSERT(to!=moves_[i].to());// Check that SIMD moves are aligned according to ABI requirements.if(LDefinition(type).isSimdType()){MOZ_ASSERT(from.isMemory()||from.isFloatReg());if(from.isMemory()){if(from.isArgument())MOZ_ASSERT(from.toArgument()->index()%SimdMemoryAlignment==0);elseMOZ_ASSERT(from.toStackSlot()->slot()%SimdMemoryAlignment==0);}MOZ_ASSERT(to.isMemory()||to.isFloatReg());if(to.isMemory()){if(to.isArgument())MOZ_ASSERT(to.toArgument()->index()%SimdMemoryAlignment==0);elseMOZ_ASSERT(to.toStackSlot()->slot()%SimdMemoryAlignment==0);}}#endifreturnmoves_.append(LMove(from,to,type));}boolLMoveGroup::addAfter(LAllocationfrom,LAllocationto,LDefinition::Typetype){// Transform the operands to this move so that performing the result// simultaneously with existing moves in the group will have the same// effect as if the original move took place after the existing moves.for(size_ti=0;i<moves_.length();i++){if(moves_[i].to()==from){from=moves_[i].from();break;}}if(from==to)returntrue;for(size_ti=0;i<moves_.length();i++){if(to==moves_[i].to()){moves_[i]=LMove(from,to,type);returntrue;}}returnadd(from,to,type);}voidLMoveGroup::printOperands(GenericPrinter&out){for(size_ti=0;i<numMoves();i++){constLMove&move=getMove(i);out.printf(" [%s -> %s",move.from().toString().get(),move.to().toString().get());#ifdef DEBUGout.printf(", %s",typeName(move.type()));#endifout.printf("]");if(i!=numMoves()-1)out.printf(",");}}